/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: ff_decoder.c
 *
 * Purpose: wrapper ffmpeg decoder for av player.
 *
 * Developer:
 *   wen.gu , 2022-01-18
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#include "avp_decoder.h"

#include "ff_fmt_convert.h"

#include "avp_log.h"

/******************************************************************************
 **    MACROS
 ******************************************************************************/

/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/
typedef struct _ff_decoder
{
    AVCodecContext* ctx;
    avp_frame_queue_t output_queue;
    avp_codec_info_t codec_info;
}ff_decoder_t;
/******************************************************************************
 **    inner FUNCTION DEFINITIONS
 ******************************************************************************/

static void ff_decoder_codec_info_copy(avp_codec_info_t* dst, const avp_codec_info_t* src)
{
    *dst = *src;    

    if (dst->extra_data && (dst->extra_data_size > 0))
    {
        dst->extra_data = av_mallocz(dst->extra_data_size + AV_INPUT_BUFFER_PADDING_SIZE);
        if (!dst->extra_data)
        {
            LOGE("alloc extra data buffer failed for ff deocder(size: %d)\n", dst->extra_data_size + AV_INPUT_BUFFER_PADDING_SIZE);
            return ;
        }
        memcpy(dst->extra_data, src->extra_data, dst->extra_data_size);
    }
}

static void ff_decoder_codec_info_free(avp_codec_info_t* info)
{
    if (info->extra_data)
    {
        av_freep(&info->extra_data);
    }
}

static GErrc ff_decoder_codec_info_to_context(AVCodecContext* ctx, const avp_codec_info_t* info)
{

    if (info->extra_data && (info->extra_data_size > 0))
    {
        ctx->extradata = (uint8_t*)av_mallocz(info->extra_data_size + AV_INPUT_BUFFER_PADDING_SIZE);
        if (!ctx->extradata)
        {
            LOGE("alloc extra data buffer failed for ff deocder(size: %d)\n", info->extra_data_size + AV_INPUT_BUFFER_PADDING_SIZE);
            return G_ErrInsufficientResources;
        }
        memcpy(ctx->extradata, info->extra_data, info->extra_data_size);
        ctx->extradata_size = info->extra_data_size;        
    }

    //sepcial item
    if (info->category == AVP_MEDIA_CATEGORY_VIDEO)
    {
        ctx->width = info->width_or_channels;
        ctx->height = info->height_or_bits_per_sample;
        ctx->framerate.den = 1;
        ctx->framerate.num = info->frame_or_sample_rate;
        
    }
    else if (info->category == AVP_MEDIA_CATEGORY_AUDIO)
    {
        ctx->sample_rate = info->frame_or_sample_rate;
        ctx->channels = info->width_or_channels;
        ctx->bits_per_coded_sample = info->height_or_bits_per_sample;
        
    }
    else
    {
        return G_ErrUnsupportedType;
    }

    //common item
    ctx->bit_rate = info->bit_rate;
    
    return G_OK;
}

/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/
GErrc ff_decoder_initialize(GPHD* handle, const avp_codec_info_t* codec_info)
{
    if (!handle || !codec_info)
    {
        return G_ErrBadParameter;
    }

    ff_decoder_t* decoder = (ff_decoder_t*)malloc(sizeof(ff_decoder_t));

    if (!decoder)
    {
        LOGE("alloc decoder instance failed\n");
        return G_ErrInsufficientResources;
    }

    enum AVCodecId codec_id = avp_codec_id_to_ff(codec_info->codec_id);

    if (codec_id == AV_CODEC_ID_NONE)
    {
        free(decoder);
        return G_ErrNotFound;
    }

    const AVCodec *codec = avcodec_find_decoder(codec_id);

    if (!codec)
    {
        free(decoder);
        return G_ErrNotFound;
    }

    decoder->ctx = avcodec_alloc_context3(codec);

    if (!decoder->ctx)
    {
        LOGE("alloc ff decoder context failed\n");
        return G_ErrInsufficientResources;
    }

    GErrc ret = ff_decoder_codec_info_to_context(decoder->ctx, codec_info);

    if (G_OK != ret)
    {
        avcodec_free_context(&decoder->ctx);
        free(decoder);
        return ret;
    }

    int res = avcodec_open2(decoder->ctx, codec, NULL);

    if (res != 0)
    {
        LOGE("open ff avcodec failed: %d\n", AVERROR(res));
        avcodec_free_context(&decoder->ctx);
        free(decoder);        
        return G_ErrInsufficientResources;
    }

    *handle = (GPHD)decoder;

    return G_OK;
}

void ff_decoder_finish(GPHD handle)
{
    if (handle)
    {
        ff_decoder_t* decoder = (ff_decoder_t*)handle;
        avcodec_free_context(&decoder->ctx);
        free(decoder);      
    }
}

GErrc ff_decoder_set_output_buffer(GPHD handle, avp_frame_queue_t frame_queue)
{
    if (handle && frame_queue)
    {
        ((ff_decoder_t*)handle)->output_queue = frame_queue;
        return G_OK;
    }    

    return G_ErrBadParameter;
}

GErrc ff_decoder_send_packet(GPHD handle, avp_packet_t* pkt)
{
    if (handle && pkt)
    {
        ff_decoder_t* decoder = (ff_decoder_t*)handle;
        AVPacket avp;
        av_init_packet(&avp);

        avp.data = pkt->buf + pkt->offset;
        avp.size = pkt->size;
        avp.dts = pkt->dts;
        avp.pts = pkt->pts;


        int res = avcodec_send_packet(decoder->ctx, &avp);

        if (res == 0)
        {
            return G_OK;
        }
        else
        {
            LOGE("send packet to ff decoder failed(%d)\n", AVERROR(res));
        }

        return G_ErrUndefined;
    }    

    return G_ErrBadParameter;
}

GErrc ff_decoder_receive_frame(GPHD handle, avp_frame_t** frame)
{
    if (handle && frame)
    {
        ff_decoder_t* decoder = (ff_decoder_t*)handle;
        avp_frame_t* out_frame = NULL;
        if (decoder->output_queue)
        {
            
        }


        return G_OK;
    }    

    return G_ErrBadParameter;
}


avp_codec_buf_provide_type_t ff_decoder_output_buffer_provide_type(GPHD handle)
{
    return AVP_CBP_TYPE_ANY;
}